import { random, flattenDeep, isArray, isFunction, isDate } from 'lodash'

export function isEmpty(val) {
  return val === '' || val === null || val === undefined
}
// 通过a 标签下载文件
export function downloadInLink(href, fileName = null) {
  if (href) {
    const link = document.createElement('a')
    link.href = href.replace('?download=0', '')
    link.download = fileName
    link.click()
  }
}
// 通过对象构造get请求参数
export function obj2QueryParams(obj) {
  if (obj) {
    const params = []
    const validTypes = ['string', 'number']
    for (const key in obj) {
      const val = obj[key]
      if (validTypes.includes(typeof val)) {
        params.push(`${key}=${val}`)
      } else if (Array.isArray(val) && !val.find(item => !validTypes.includes(typeof item))) {
        // 只针对可以一维值数组进行处理
        params.push(`${key}=${val.join(',')}`)
      }
    }
    return params.join('&')
  }
  return ''
}

export function guid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0
    const v = c === 'x' ? r : (r & 0x3) | 0x8
    return v.toString(16)
  })
}

export function formatDate(date, fmt) {
  if (!date) {
    return
  }
  if (typeof date === 'string') {
    date = str2Date(date)
  } else if (typeof date === 'number') {
    date = new Date(date)
  }
  if (date instanceof Date && !isNaN(date.getTime())) {
    if (!fmt) {
      fmt = 'yyyy-MM-dd hh:mm:ss'
    }
    if (/(y+)/.test(fmt)) {
      fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substring(4 - RegExp.$1.length))
    }
    const o = {
      'M+': date.getMonth() + 1,
      'd+': date.getDate(),
      'h+': date.getHours(),
      'm+': date.getMinutes(),
      's+': date.getSeconds(),
    }
    for (const k in o) {
      if (new RegExp(`(${k})`).test(fmt)) {
        const str = o[k] + ''
        fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str))
      }
    }
    return fmt
  } else {
    return null
  }
}

export function padLeftZero(str, minLen = 2) {
  if (typeof str === 'number') {
    str = str.toString()
  }
  if (str.indexOf('.') === -1 && str.length < minLen) {
    const zeroArr = []
    for (let i = 0; i < minLen - str.length; i++) {
      zeroArr.push('0')
    }
    return zeroArr.join('') + str
  }
  return str
}

export function getMonthEndDate(date) {
  if (!date) {
    return
  }
  if (typeof date === 'string') {
    date = str2Date(date)
  } else if (typeof date === 'number') {
    date = new Date(date)
  }
  const year = date.getFullYear()
  const month = date.getMonth()
  const endTime = new Date(year, month + 1, 0)
  // let endTime = new Date(year, month - 1, firstDay.getDate()).valueOf() / 1000 + 24 * 60 * 60;
  return formatDate(endTime, 'yyyy-MM-dd')
}

export function str2Date(dateStr) {
  // ie 中需要补全2位
  dateStr = dateStr.replace(/-(\d)(?!\d)/g, '-0$1')
  // if (/:/.test(dateStr)) {
  // ie 带时分秒时，解析 “-”、“T” 会失败，并且要去掉毫秒部分
  dateStr = dateStr.replace(/T/g, ' ').replace(/Z/g, '').replace(/-/g, '/').replace(/\.\d+/, '').replace(/\+\d+/, '')
  // }
  // }
  // 格式化不带分隔符的字符串
  // 只支持 完整双位值的字符串 20220103  20220103092804
  if (dateStr.indexOf('/') === -1) {
    const strArr = []
    for (const i in dateStr) {
      if (i < 14) {
        strArr.push(dateStr[i])
        if (i === '3' || i === '5') {
          strArr.push('/')
        }
        if (i === '7') {
          strArr.push(' ')
        }
        if (i === '9' || i === '11') {
          strArr.push(':')
        }
      }
    }
    dateStr = strArr.join('').trim()
  }
  let result = new Date(dateStr)
  if (isNaN(result.getTime())) {
    // fireFox 中，在缺位时使用 / 分割会失败，转为 -
    result = new Date(dateStr.replace(/\//g, '-'))
  }
  return result
}

export function getFileTypeByName(name) {
  if (name) {
    const dotIndx = name.lastIndexOf('.')
    if (dotIndx !== -1) {
      return name.substring(dotIndx + 1).toLowerCase()
    }
  }
}

export function copy2Clipboard(val) {
  const copyFn = e => {
    e.preventDefault()
    e.clipboardData.setData('text/plain', val)
    document.removeEventListener('copy', copyFn)
  }
  document.addEventListener('copy', copyFn)
  document.execCommand('Copy')
}
/**
 * 语义化描述指定时间
 * 返回格式
 *  1. 所在周小于2个自然周时
 *    【今天/明天/后天】【上午/中午/下午】【时间】
 *    【本周/下周（周几）】【上午/中午/下午】【时间】
 * 2. 超出2周 直接展示时间
 */
export function getTimeGapDesc(timeStr) {
  // TODO 未对时间格式校验
  if (timeStr) {
    const nowDate = new Date(formatDate(new Date(), 'yyyy-MM-dd 00:00:00'))
    const targetDate = new Date(formatDate(timeStr, 'yyyy-MM-dd 00:00:00'))
    const nowVal = nowDate.getTime()
    const targetVal = targetDate.getTime()
    const dayGap = 86400000
    const weekDict = ['一', '二', '三', '四', '五', '六', '日']
    let nowWeekDay = nowDate.getDay()
    if (nowWeekDay === 0) {
      nowWeekDay = 7
    }

    let targetWeekDay = targetDate.getDay()
    if (targetWeekDay === 0) {
      targetWeekDay = 7
    }
    const currWeekStart = new Date(nowVal - (nowWeekDay - 1) * dayGap)
    let dayDesc = ''
    if (targetVal - nowVal < dayGap) {
      dayDesc = '今天'
    } else if (targetVal - nowVal < dayGap * 2) {
      dayDesc = '明天'
    } else if (targetVal - nowVal < dayGap * 3) {
      dayDesc = '后天'
    } else if (targetVal - currWeekStart < dayGap * 7) {
      dayDesc = '本周' + weekDict[targetWeekDay - 1]
    } else if (targetVal - currWeekStart < dayGap * 14) {
      dayDesc = '下周' + weekDict[targetWeekDay - 1]
    } else {
      return timeStr
    }
    const targetTimeStr = formatDate(timeStr, 'hh:mm')
    let timeDesc = '下午'
    if (targetTimeStr < '12:00:00') {
      timeDesc = '上午'
    } else if (targetTimeStr < '13:00:00') {
      timeDesc = '中午'
    }

    return `${dayDesc} ${timeDesc} ${targetTimeStr}`
  }
  return ''
}

export function debounce(func, wait, immediate = true) {
  let timer

  const fn = function () {
    const context = this
    const args = arguments

    if (timer) {
      clearTimeout(timer)
    }
    if (immediate) {
      const callNow = !timer
      timer = setTimeout(() => {
        timer = null
      }, wait)
      if (callNow) {
        func.apply(context, args)
      }
    } else {
      timer = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }
  }
  // if (immediate) {
  //   fn()
  // }
  return fn
}

// 构造要合并的rows
// 递归寻找要合并的列，按 col 依次查找
export function getVxeTableMergeCells(tableData, startcolIdx, childField = 'childList') {
  const mergeCells = []
  let currRowIdx = 0
  tableData.forEach(item => {
    _getChildRowCount(item, currRowIdx, startcolIdx)
  })
  function _getChildRowCount(rowData, rowIdx, colIdx) {
    const childList = rowData[childField]
    if (childList && childList.length) {
      let totalRows = 0
      childList.forEach(cItem => {
        const nextCol = colIdx + 1
        const childRows = _getChildRowCount(cItem, rowIdx + totalRows, nextCol)
        totalRows += childRows
      })
      if (totalRows > 1) {
        mergeCells.push({
          row: rowIdx,
          col: colIdx,
          rowspan: totalRows,
        })
      }
      return totalRows
    } else {
      currRowIdx += 1
      return 1
    }
  }
  return mergeCells
}

export function parseJsonStr(str) {
  if (str) {
    try {
      return JSON.parse(str)
    } catch {
      return null
    }
  }
  return null
}

export function isJsonString(str) {
  if (typeof str === 'string') {
    try {
      const obj = JSON.parse(str)
      if (obj && typeof obj === 'object') {
        return { isJson: true, result: obj }
      }
    } catch {
      return { isJson: false }
    }
  }
  return { isJson: false }
}

export function stringifyObj(obj) {
  if (obj && Object.keys(obj).length) {
    try {
      return JSON.stringify(obj)
    } catch {
      return null
    }
  }
  return null
}

/**
 * @function 表单下载功能
 * @param list {Array} 文件列表
 * @param type {String} 判断是否通过后端接口
 */
export function downloadFileByUrl(list, type) {
  // 通过接口调用
  if (type && type === 'api') {
    // for (let i = 0; i < list.length; i++) {
    //   let { id, docName, docType, sourceFilePath } = list[i] || {};
    //   let param = {
    //     docId: id,
    //     fileName: docName,
    //     filePath: sourceFilePath,
    //   };
    //   downLoadRecordFile(param).then(res => {
    //     if (res) {
    //       let xml = new XMLHttpRequest();
    //       xml.responseType = 'blob';
    //       xml.open('GET', sourceFilePath);
    //       xml.send();
    //       xml.addEventListener('load', () => {
    //         let a = document.createElement('a');
    //         let _url = URL.createObjectURL(xml.response);
    //         a.href = _url;
    //         a.download = `${docName}.${docType}`;
    //         a.click();
    //       });
    //     }
    //   });
    // }
  } else {
    for (let i = 0; i < list.length; i++) {
      const file = list[i]
      const xml = new XMLHttpRequest()
      xml.responseType = 'blob'
      xml.open('GET', file.sourcePath || file.thumbnailPath)
      xml.send()
      xml.addEventListener('load', () => {
        const a = document.createElement('a')
        const _url = URL.createObjectURL(xml.response)
        a.href = _url
        a.download =
          file.name.indexOf('.pdf') > -1 || file.name.indexOf('.docx') > -1
            ? `${file.name}`
            : `${file.name}.${file.fileType}`
        a.click()
      })
    }
  }
}

// 根据关键字模糊查找数组中的对应项
export function matchArrayItemByKeywords(keywords, array, field) {
  if (keywords && array && Array.isArray(array)) {
    // 直接查找
    let orgItem = IncludesInArray(keywords, array, field)
    if (!orgItem) {
      // 掐头去尾匹配中间
      const start = random(0, keywords.length / 3, false)
      const end = keywords.length - 2
      let keywords1
      if (start < end) {
        keywords1 = keywords.substring(start, end)
      } else {
        keywords1 = keywords.substring(start)
      }
      orgItem = IncludesInArray(keywords1, array, field)

      if (!orgItem) {
        // 取前两位
        const keywords2 = keywords.substring(0, 2)
        orgItem = IncludesInArray(keywords2, array, field)
      }
      if (!orgItem) {
        // 取后两位
        const keywords3 = keywords.substring(keywords.length - 3)
        orgItem = IncludesInArray(keywords3, array, field)
      }
    }
    return orgItem
  }
  return null
}

export function IncludesInArray(keywords, array, field) {
  if (keywords && array && Array.isArray(array)) {
    return array.find(item => {
      if (field) {
        return item[field].includes(keywords)
      } else {
        return item.includes(keywords)
      }
    })
  }
}

export function stripHTML(str) {
  const reg = /<(?:.|\s)*?>/g
  return str.replace(reg, '')
}

export const replaceTag2Flag = (val, tag, flag) => {
  if (tag) {
    const reg = new RegExp(`<${tag}(?:.|\\s)*?>`, 'g')
    return val.replace(reg, flag)
  }
}

export const randomPassword = (passwordLength = 10) => {
  let password = ''
  let randomNum = 0
  const chars = '0123456789abcdefghijklmnopqrstuvwxtz!@#$%^&*+=_.()<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  for (let index = 0; index < passwordLength; index++) {
    randomNum = Math.floor(Math.random() * chars.length)
    password += chars.charAt(randomNum, randomNum + 1)
  }
  if (
    !password ||
    !/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*+=_.()<>?])[\da-zA-Z!@#$%^&*+=_.()<>?]{8,16}$/.test(password)
  ) {
    return randomPassword(passwordLength)
  } else {
    return password
  }
}

// range [min, max]
export const isRangesIntersection = (range1, range2) => {
  if (range1 && range2) {
    const [r1min, r1max] = range1
    const [r2min, r2max] = range2
    if (r1max < r2min || r2max < r1min) {
      return false
    }
    return true
  }
  return false
}

export const countNodesByLevel = (treeData, options = { childField: 'children', childFilter: null }) => {
  const levelCounts = {}
  if (!options.childField) {
    options.childField = 'children'
  }

  function traverse(node, level) {
    if (!node) return

    if (options.childFilter) {
      if (options.childFilter(node)) {
        levelCounts[level] = (levelCounts[level] || 0) + 1
      }
    } else {
      levelCounts[level] = (levelCounts[level] || 0) + 1
    }
    const children = isArray(node) ? node : node[options.childField]
    if (children) {
      for (const child of children) {
        if (options.childFilter && options.childFilter(child)) {
          traverse(child, level + 1)
        } else {
          traverse(child, level + 1)
        }
      }
    }
  }

  traverse(treeData, 0)

  return levelCounts
}

export const findChildItem = (obj, idVal, options = { fieldId: 'id', fieldChild: 'children' }) => {
  const { fieldId = 'id', fieldChild = 'children' } = options
  if (Array.isArray(obj)) {
    let result = null
    for (let i = 0, len = obj.length; i < len; i += 1) {
      result = findChildItem(obj[i], idVal, options)
      if (result) {
        return result
      }
    }
  } else {
    if (obj[fieldId] === idVal) {
      return {
        parent: null,
        node: obj,
      }
    }
    const children = obj[fieldChild]
    if (children) {
      for (let i = 0, len = children.length; i < len; i += 1) {
        const res = findChildItem(children[i], idVal, options)
        if (res) {
          return {
            parent: res.parent || obj,
            node: res.node,
          }
        }
      }
    }
  }
  return null
}

export const findFirstLeafChild = (obj, fieldChild = 'children') => {
  if (Array.isArray(obj)) {
    let result = null
    for (let i = 0, len = obj.length; i < len; i += 1) {
      result = findFirstLeafChild(obj[i], fieldChild)
      if (result) {
        return result
      }
    }
  } else {
    const children = obj[fieldChild]
    if (children && children.length) {
      for (let i = 0, len = children.length; i < len; i += 1) {
        const result = findFirstLeafChild(children[i], fieldChild)
        if (result) {
          return result
        }
      }
    } else {
      return obj
    }
  }
  return null
}

export const flattenTreeNode = (root, childField = 'children') => {
  const children = root[childField]
  let result = [root]
  if (children?.length) {
    result = result.concat(...children.map(citem => flattenTreeNode(citem, childField)))
  }
  return result
}

export const diffChangedVals = (source, traget, diffFields) => {
  let isEquel = true
  const changedItems = []
  if (source !== null && traget !== null) {
    if (typeof source === typeof traget) {
      const isArrayType = Array.isArray(source)
      if (isArrayType) {
        source.forEach((item, idx) => {
          changedItems.push(...diffChangedVals(item, traget[idx], diffFields))
        })
      } else if (typeof source === 'object') {
        for (const key in source) {
          if (Object.hasOwnProperty.call(source, key)) {
            const el = source[key]
            const targetEl = traget[key]
            let isCompare = true
            if (Array.isArray(diffFields) && diffFields.length) {
              isCompare = diffFields.includes(key)
            }
            if (isCompare) {
              if (typeof el !== 'object') {
                if (isEquel) {
                  isEquel = el === targetEl
                  if (!isEquel) {
                    console.log('%c⧭', 'color: #731d6d', key, el, targetEl)
                  }
                }
              } else {
                changedItems.push(...diffChangedVals(el, targetEl, diffFields))
              }
            }
          }
        }
      } else {
        isEquel = source === traget
      }
    }
  }
  // console.log('%c⧭', 'color: #006dcc', isEquel, source)
  if (!isEquel) {
    changedItems.push(source)
  }
  return flattenDeep(changedItems)
}

export function fixNumber(num, digits = 0) {
  if (typeof num === 'string') {
    num = Number(num)
  }
  if (typeof num === 'number' && !isNaN(num)) {
    return Number(num.toFixed(digits))
  }
  return num
}

export function getPermissions(list, codes) {
  list.forEach(item => {
    codes.push(item.belongPage)
    if (item.menuVOS && item.menuVOS.length) {
      getPermissions(item.menuVOS, codes)
    }
  })
  return codes
}

export const isMobile = () => {
  let ismobile = false
  if (navigator.userAgent || window.matchMedia) {
    ismobile =
      /android|iphone|mobile|symbianos|windows phone|ipad|ipod/i.test(navigator.userAgent) ||
      window.matchMedia('(max-width: 640px)').matches
  }
  return ismobile
}

export function groupArray(arr, options = { gid: 'pid', gname: 'pname', childField: 'children', itemBuildFn: null }) {
  const groups = []
  const { gid = 'pid', gname = 'pname', childField = 'children', itemBuildFn } = options
  arr.forEach(item => {
    let gitem = groups.find(group => group.id === item[gid])
    if (!gitem) {
      gitem = {
        id: item[gid],
        name: item[gname] || '未分类',
        [childField]: [],
      }
      if (!item[gname]) {
        groups.unshift(gitem)
      } else {
        groups.push(gitem)
      }
    }
    if (itemBuildFn && isFunction(itemBuildFn)) {
      gitem[childField].push(itemBuildFn(item))
    } else {
      gitem[childField].push(item)
    }
  })
  return groups
}

export function isDayBeforeNow(time) {
  if (time && isDate(time)) {
    const now = new Date()
    // 按0点计算
    now.setHours(0, 0, 0, 0)
    return time.getTime() < now.getTime()
  }
  return false
}

export function isDayLaterNow(time) {
  if (time && isDate(time)) {
    const now = new Date()
    // 按0点计算
    now.setHours(0, 0, 0, 0)
    return time.getTime() > now.getTime()
  }
  return false
}
